from fastapi import APIRouter, UploadFile, File, HTTPException, Query
from fastapi.responses import JSONResponse
import pandas as pd
import io
import os
import logging
import numpy as np
import shutil
from typing import Dict, Any, List
from pathlib import Path
import json

router = APIRouter()
logger = logging.getLogger(__name__)

# 确保上传目录存在
UPLOAD_DIR = Path("uploads")
UPLOAD_DIR.mkdir(exist_ok=True)

def convert_nan_to_none(obj):
    try:
        if isinstance(obj, (np.floating, float)):
            return None if np.isnan(obj) else float(obj)
        elif isinstance(obj, pd.Timestamp):
            return obj.isoformat()
        elif isinstance(obj, (np.ndarray, pd.Series)):
            # 对numpy数组和pandas Series的特殊处理
            return obj.tolist() if hasattr(obj, 'tolist') else [convert_nan_to_none(x) for x in obj]
        elif isinstance(obj, pd.DataFrame):
            # 对 DataFrame 的处理
            return convert_nan_to_none(obj.to_dict(orient='records'))
        elif isinstance(obj, dict):
            return {k: convert_nan_to_none(v) for k, v in obj.items()}
        elif isinstance(obj, list):
            return [convert_nan_to_none(item) for item in obj]
        # 谨慎使用pd.isna，确保它只应用于标量值
        elif isinstance(obj, (str, int, float, bool, type(None))):
            try:
                return None if pd.isna(obj) else obj
            except:
                return obj
        return obj
    except Exception as e:
        logger.error(f"转换过程中出错: {str(e)}，对象类型: {type(obj)}")
        # 如果转换失败，尝试返回原始对象的字符串表示
        try:
            return str(obj)
        except:
            return None

@router.post("/upload")
async def upload_file(file: UploadFile = File(...), sheet_name: str = None):
    try:
        logger.info(f"开始处理文件上传: {file.filename}")
        
        # 创建唯一的文件名
        file_extension = os.path.splitext(file.filename)[1]
        unique_filename = f"{file.filename.replace(file_extension, '')}_{id(file)}{file_extension}"
        file_path = UPLOAD_DIR / unique_filename
        
        logger.info(f"将文件保存到: {file_path.absolute()}")
        
        # 确保上传目录存在
        UPLOAD_DIR.mkdir(exist_ok=True, parents=True)
        
        # 保存上传的文件
        with open(file_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
        
        # 获取绝对路径
        absolute_path = file_path.absolute()
        logger.info(f"文件已保存到: {absolute_path}")
        
        # 根据文件类型处理数据
        try:
            if file.filename.endswith('.csv'):
                logger.info("处理 CSV 文件")
                df = pd.read_csv(absolute_path)
            elif file.filename.endswith(('.xlsx', '.xls')):
                logger.info("处理 Excel 文件")
                # 使用更安全的方式读取Excel文件，处理各种数据类型
                try:
                    # 如果指定了工作表名称，则使用该工作表
                    if sheet_name:
                        logger.info(f"使用指定的工作表: {sheet_name}")
                        df = pd.read_excel(absolute_path, sheet_name=sheet_name, parse_dates=True)
                    else:
                        logger.info("未指定工作表，使用第一个工作表")
                        df = pd.read_excel(absolute_path, parse_dates=True)
                    
                    # 特殊类型处理
                    # 1. 处理日期时间列
                    for col in df.select_dtypes(include=['datetime64']).columns:
                        df[col] = df[col].astype(str)
                    
                    # 2. 处理复杂对象列
                    for col in df.columns:
                        # 检查每列的数据类型，如果是复杂类型则转为字符串
                        if df[col].dtype == 'object':
                            try:
                                # 尝试检测是否为JSON可序列化对象
                                df[col] = df[col].apply(lambda x: 
                                    json.dumps(x) if not pd.isna(x) and not isinstance(x, (str, int, float, bool)) 
                                    else x)
                            except Exception as type_err:
                                logger.warning(f"列 {col} 类型转换失败: {str(type_err)}")
                    
                    logger.info(f"Excel文件读取成功，共{len(df)}行，{len(df.columns)}列")
                except Exception as excel_err:
                    logger.error(f"Excel文件读取失败: {str(excel_err)}")
                    raise HTTPException(status_code=400, detail=f"Excel文件处理失败: {str(excel_err)}")
            else:
                raise HTTPException(status_code=400, detail="不支持的文件类型")
        except Exception as e:
            logger.error(f"文件处理失败: {str(e)}")
            raise HTTPException(status_code=400, detail=f"文件处理失败: {str(e)}")
        
        # 转换为字典列表并处理 NaN 值
        logger.info("开始转换数据格式...")
        try:
            # 先转换为records形式的字典
            data = df.to_dict(orient='records')
            # 处理特殊值和NaN
            data = convert_nan_to_none(data)
            
            # 确保所有列名都是字符串类型
            columns = [str(col) for col in df.columns.tolist()]
            
            # 记录处理后的数据大小
            import sys
            data_size = sys.getsizeof(str(data)) / (1024 * 1024)  # 大小（MB）
            logger.info(f"数据处理完成，共 {len(data)} 行，数据大小约 {data_size:.2f} MB")
            
            if data_size > 10:  # 如果数据超过10MB
                logger.warning("数据量较大，可能导致性能问题")
                # 只保留前100行用于预览
                preview_data = data[:100]
                logger.info(f"生成预览数据，保留前100行")
                
                return JSONResponse({
                    "message": "文件上传成功（数据量较大，仅返回部分预览）",
                    "file_path": str(absolute_path),
                    "data": preview_data,
                    "columns": columns,
                    "rows": len(df),
                    "is_preview": True,
                    "preview_rows": len(preview_data)
                })
            
            return JSONResponse({
                "message": "文件上传成功",
                "file_path": str(absolute_path),
                "data": data,
                "columns": columns,
                "rows": len(df)
            })
            
        except Exception as convert_err:
            logger.error(f"数据转换失败: {str(convert_err)}")
            import traceback
            logger.error(f"数据转换详细错误: {traceback.format_exc()}")
            
            # 提供有限的数据作为响应
            return JSONResponse({
                "message": "文件上传成功，但数据转换出现问题",
                "file_path": str(absolute_path),
                "error": str(convert_err),
                "columns": [str(col) for col in df.columns.tolist()],
                "rows": len(df),
                "data": [] # 空数据，避免错误
            })
        
    except HTTPException as he:
        logger.error(f"HTTP 错误: {str(he)}")
        raise he
    except Exception as e:
        logger.error(f"服务器错误: {str(e)}")
        import traceback
        logger.error(f"详细错误信息: {traceback.format_exc()}")
        raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")

@router.get("/excel_sheets")
async def get_excel_sheets(file_path: str = Query(..., description="Excel文件路径")):
    """
    获取Excel文件中的所有工作表名称
    """
    try:
        if not file_path:
            raise HTTPException(status_code=400, detail="未提供文件路径")
        
        # 验证文件路径是否存在
        file_path = Path(file_path)
        if not file_path.exists():
            raise HTTPException(status_code=404, detail=f"文件不存在: {str(file_path)}")
        
        # 验证是否是Excel文件
        if not str(file_path).lower().endswith(('.xlsx', '.xls')):
            raise HTTPException(status_code=400, detail="提供的文件不是Excel文件")
        
        # 使用pandas读取Excel文件的工作表名称
        try:
            # 只读取Excel的sheet_names，不读取数据
            excel_file = pd.ExcelFile(file_path)
            sheet_names = excel_file.sheet_names
            
            logger.info(f"成功获取Excel文件工作表列表，共{len(sheet_names)}个表: {', '.join(sheet_names)}")
            
            return JSONResponse({
                "message": "获取工作表名称成功",
                "sheet_names": sheet_names
            })
            
        except Exception as e:
            logger.error(f"读取Excel工作表名称失败: {str(e)}")
            raise HTTPException(status_code=500, detail=f"读取Excel工作表名称失败: {str(e)}")
            
    except HTTPException as he:
        raise he
    except Exception as e:
        logger.error(f"获取Excel工作表列表发生错误: {str(e)}")
        import traceback
        logger.error(f"详细错误信息: {traceback.format_exc()}")
        raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}") 